#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <pthread.h>
#include "cJSON.h"
#include <assert.h>
#define N 64

// 新连接处理线程函数
void *new_sock(void *arg)
{
    // 打印提示信息，表示即将接收到新的连接
    printf("将要接收到新的连接\n");
    int sockfd, qtfilefd;
    char buf[1200];
    int addrlen = sizeof(struct sockaddr);
    struct sockaddr_in addr, clientaddr;
    int nbytes;

    // 1. 创建套接字
    // 使用 AF_INET（IPv4）地址族，SOCK_STREAM（可靠的面向连接的字节流）类型，协议为 0（自动选择）
    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd < 0)
    {
        // 如果创建套接字失败，打印错误信息并退出线程
        perror("socket err");
        pthread_exit(NULL);
    }

    // 设置端口重用选项
    int reuse_addr = 1;
    if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &reuse_addr, sizeof(reuse_addr)) < 0)
    {
        perror("setsockopt for SO_REUSEADDR failed");
        close(sockfd);
        pthread_exit(NULL);
    }

    // 2. 定义套接字地址
    // 清空地址结构体，设置地址族为 AF_INET，IP 地址为特定的 IP（192.168.51.220），端口号为 6666
    bzero(&addr, addrlen);
    addr.sin_family = AF_INET;
    addr.sin_addr.s_addr = inet_addr("192.168.51.245");
    addr.sin_port = htons(6666);

    // 3. 绑定套接字
    // 将创建的套接字与指定的地址结构体进行绑定，如果绑定失败，打印错误信息，关闭套接字并退出线程
    if (bind(sockfd, (struct sockaddr *)&addr, addrlen) < 0)
    {
        perror("bind err");
        close(sockfd);
        pthread_exit(NULL);
    }

    // 4. 启动监听
    // 在绑定的套接字上开始监听连接请求，最大连接请求队列长度为
    // 5，如果监听失败，打印错误信息，关闭套接字并退出线程
    if (listen(sockfd, 5) < 0)
    {
        perror("listen err");
        close(sockfd);
        pthread_exit(NULL);
    }

    // 5. 接收连接
    // 接受一个客户端的连接请求，将客户端的地址信息存储在 clientaddr 中，连接描述符存储在 qtfilefd 中
    // 如果接受连接失败，打印错误信息，关闭套接字并退出线程
    qtfilefd = accept(sockfd, (struct sockaddr *)&clientaddr, &addrlen);
    if (qtfilefd < 0)
    {
        perror("accept err");
        close(sockfd);
        pthread_exit(NULL);
    }
    // 打印提示信息，表示接收到了 QT 客户端的连接
    printf("接收到 QT 客户端\n");

    // 6. 收发点表
    // 清空缓冲区，准备接收数据
    bzero(buf, 1200);
    ssize_t recvBytes = recv(qtfilefd, buf, 1200, 0);
    if (recvBytes > 0)
    {
        
        char sys_cmd[256];
        char *path="/tmp/ipc/node/";
        char *name="node.json";
        char end_path[128];
        int ret;
        
        // 判断文件是否存在
        assert(NULL != name && strlen(name) > 0);

        sprintf(end_path, "%s%s", path, name);
        // 文件不存在创建
        if (access(end_path, F_OK) < 0)
        {
            sprintf(sys_cmd, "%s %s", "touch", name);
            ret=system(sys_cmd);
        }
        if(ret!=0)
        {
            printf("fuck!\n");
            return NULL;
        }
        printf("复制中．．．\n");
        // 打开文件用于写入，文件路径为指定路径，打开模式为可读可写、如果不存在则创建、如果存在则截断文件，权限为 777

        int fd = open(end_path, O_RDWR | O_CREAT | O_TRUNC);
        if (fd < 0)
        {
            // 如果打开文件失败，打印错误信息，关闭连接描述符和套接字并退出线程
            perror("打开文件失败");
            close(qtfilefd);
            close(sockfd);
            pthread_exit(NULL);
        }
        ssize_t writeBytes = write(fd, buf, recvBytes);
        if (writeBytes != recvBytes)
        {
            // 如果写入文件的字节数与接收到的字节数不一致，打印错误信息，关闭文件描述符、连接描述符和套接字并退出线程
            perror("写入文件失败");
            close(fd);
            close(qtfilefd);
            close(sockfd);
            pthread_exit(NULL);
        }
        // 关闭文件描述符
        close(fd);
        // 打印提示信息，表示复制成功
        printf("复制成功!\n");
    }
    else
    {
        // 如果接收数据失败，打印错误信息，关闭连接描述符和套接字并退出线程
        perror("复制失败\n");
        close(qtfilefd);
        close(sockfd);
        pthread_exit(NULL);
    }
    // 退出线程
    pthread_exit(NULL);
}

int main(int argc, char const *argv[])
{
    int broadfd;

    // 创建 UDP 套接字
    // 创建一个使用 AF_INET（IPv4）地址族、SOCK_DGRAM（无连接的数据报套接字）类型、协议为 0（自动选择）的套接字描述符
    broadfd = socket(AF_INET, SOCK_DGRAM, 0);
    if (broadfd < 0)
    {
        // 如果创建套接字失败，打印错误信息并返回 -1
        perror("socket err");
        return -1;
    }

    // 绑定套接字
    struct sockaddr_in addr;
    // 设置地址结构体的地址族为 AF_INET、端口号为 8888、IP 地址为任意地址（INADDR_ANY）
    addr.sin_family = AF_INET;
    addr.sin_port = htons(8888);
    addr.sin_addr.s_addr = INADDR_ANY;

    int addrlen = sizeof(addr);

    // 设置 UDP 端口重用选项
    // int reuse_addr_udp = 1;
    // if (setsockopt(broadfd, SOL_SOCKET, SO_REUSEADDR, &reuse_addr_udp, sizeof(reuse_addr_udp)) < 0)
    // {
    //     perror("setsockopt for UDP SO_REUSEADDR failed");
    //     close(broadfd);
    //     return -1;
    // }

    // 将创建的 UDP 套接字与指定的地址结构体进行绑定，如果绑定失败，打印错误信息，关闭套接字并返回 -1
    if (bind(broadfd, (struct sockaddr *)&addr, addrlen) < 0)
    {
        perror("bind err");
        close(broadfd);
        return -1;
    }

    ssize_t len;
    char buf[N] = {0};
    struct sockaddr_in cliaddr;

    // 接收搜索包
    // 清空缓冲区，准备接收数据
    bzero(buf, N);
    // 从 UDP 套接字接收数据报，将接收到的数据存储在 buf 中，最大长度为 N，接收的数据来源的地址信息存储在 cliaddr 中
    len = recvfrom(broadfd, buf, N, 0, (struct sockaddr *)&cliaddr, &addrlen);

    // 判断是否是本公司产品
    // 如果接收到的数据不等于 "group"，打印提示信息，表示不是本公司产品，关闭 UDP 套接字并返回 -1
    if (strcmp(buf, "group") != 0)
    {
        printf("不是我的公司\n");
        close(broadfd);
        return -1;
    }

    // 回复 yes
    char buf1[] = "yes";
    ssize_t sendBytes = sendto(broadfd, buf1, sizeof(buf1), 0, (struct sockaddr *)&cliaddr, addrlen);
    if (sendBytes != sizeof(buf1))
    {
        // 如果发送回复的数据长度与预期不一致，打印错误信息，关闭 UDP 套接字并返回 -1
        perror("发送回复失败");
        close(broadfd);
        return -1;
    }
    bzero(buf, N);

    // 创建 TCP 套接字
    // 创建一个使用 AF_INET（IPv4）地址族、SOCK_STREAM（可靠的面向连接的字节流）类型、协议为 0（自动选择）的套接字描述符
    int tcpfd = socket(AF_INET, SOCK_STREAM, 0);
    if (tcpfd < 0)
    {
        // 如果创建套接字失败，打印错误信息，关闭 UDP 套接字并返回 -1
        perror("sock err");
        close(broadfd);
        return -1;
    }

    // 设置 TCP 端口重用选项
    int reuse_addr_tcp = 1;
    if (setsockopt(tcpfd, SOL_SOCKET, SO_REUSEADDR, &reuse_addr_tcp, sizeof(reuse_addr_tcp)) < 0)
    {
        perror("setsockopt for TCP SO_REUSEADDR failed");
        close(tcpfd);
        close(broadfd);
        return -1;
    }

    // 绑定套接字
    // 将创建的 TCP 套接字与前面相同的地址结构体进行绑定，如果绑定失败，打印错误信息，关闭 TCP 套接字和 UDP 套接字并返回 -1
    if (bind(tcpfd, (struct sockaddr *)&addr, addrlen) < 0)
    {
        perror("bind err");
        close(tcpfd);
        close(broadfd);
        return -1;
    }

    // 监听套接字
    // 在绑定的 TCP 套接字上开始监听连接请求，最大连接请求队列长度为
    // 5，如果监听失败，打印错误信息，关闭 TCP 套接字和 UDP 套接字并返回 -1
    if (listen(tcpfd, 5) < 0)
    {
        perror("listen err");
        close(tcpfd);
        close(broadfd);
        return -1;
    }

    // 接收客户端的连接
    // 打印提示信息，表示正在等待 QT 客户端连接
    printf("等待 QT 客户端连接\n");
    int clifd;

    // 接收对端的地址
    // 接受一个客户端的连接请求，连接描述符存储在 clifd 中，由于不关心客户端的具体地址信息，所以后两个参数为 NULL
    clifd = accept(tcpfd, NULL, NULL);
    if (clifd < 0)
    {
        // 如果接受连接失败，打印错误信息，关闭 TCP 套接字和 UDP 套接字并返回 -1
        perror("accept err");
        close(tcpfd);
        close(broadfd);
        return -1;
    }

    // 接收到 QT 端链接
    // 打印提示信息，表示 QT 客户端连接即将到来
    printf("QT 客户端连接即将到来\n");
    char buf2[] = "yes";
    sendBytes = send(clifd, buf2, sizeof(buf2), 0);
    if (sendBytes != sizeof(buf2))
    {
        // 如果发送确认消息的数据长度与预期不一致，打印错误信息，关闭连接描述符、TCP 套接字和 UDP 套接字并返回 -1
        perror("发送确认消息失败");
        close(clifd);
        close(tcpfd);
        close(broadfd);
        return -1;
    }

    while (1)
    {
        bzero(buf, N);
        int len = recv(clifd, buf, N, 0);
        if (len < 0)
        {
            // 如果接收数据失败，打印错误信息，继续循环等待下一次接收
            perror("接收数据失败");
            continue;
        }
        else if (len == 0)
        {
            // 如果接收的数据长度为 0，表示客户端断开连接，打印提示信息，跳出循环
            printf("客户端断开连接\n");
            break;
        }
        cJSON *root = cJSON_Parse(buf);
        if (root == NULL)
        {
            // 如果解析 JSON 数据失败，打印提示信息，继续循环等待下一次接收
            printf("解析 JSON 失败\n");
            continue;
        }
        cJSON *item = cJSON_GetObjectItem(root, "type");
        if (item == NULL)
        {
            // 如果在 JSON 数据中找不到 "type" 字段，打印提示信息，释放 JSON 对象内存，继续循环等待下一次接收
            printf("找不到 type 字段\n");
            cJSON_Delete(root);
            continue;
        }
        int type = item->valueint;
        if (type == 1) // tcp 连接
        {
            // 如果 "type" 字段的值为 1，表示 tcp 连接成功，打印提示信息，创建一个新线程处理后续操作
            printf("tcp 连接成功\n");
            pthread_t tid;
            if (pthread_create(&tid, NULL, new_sock, NULL) != 0)
            {
                // 如果创建线程失败，打印错误信息
                perror("创建线程失败");
            }
        }
        else if (type == 3) // 客户端存活
        {
            // 如果 "type" 字段的值为 3，表示客户端存活，发送 "living" 消息给客户端
            if (send(clifd, "living", 7, 0) < 0)
            {
                // 如果发送消息失败，打印错误信息
                perror("send err\n");
            }
        }
        // 释放 JSON 对象内存
        cJSON_Delete(root);
    }

    // 关闭连接描述符、TCP 套接字和 UDP 套接字
    close(clifd);
    close(tcpfd);
    close(broadfd);

    return 0;
}